package ice.server.comm;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 将JPSQLModel解析为JPQL语句的解析器
 * @author zhangrb
 * @user zhangrb
 * @date 2019-8-26
 */
public class JPSQLModelParser{

    private static Logger log = LoggerFactory.getLogger(JPSQLModelParser.class);

    /**
     * 解析方法
     * @param m JPSQLModel对象
     * @return 解析之后的结果对象
     */
    public static JPQLParseResult parseToJPQL(JPSQLModel m) {
        StringBuffer jpql = new StringBuffer();
        List<Object> paramList = new ArrayList<>();
        appendResult(m, jpql);
        appendForm(m, jpql);
        appendWhere(m, jpql, paramList);
        appendGroup(m, jpql);
        appendHaving(m, jpql, paramList);
        appendOrder(m, jpql);
        log.debug("jpql===" + jpql.toString());
        JPQLParseResult jr = new JPQLParseResult();
        jr.setJpql(jpql.toString());
        jr.setParamList(paramList);
        return jr;
    }

    /**
     * 解析order语句
     * @param m JPSQLModel对象
     * @param jpql jpql语句buffer
     * @author zhangrb
     * @user zhangrb
     * @date 2019-08-26
     */
    private static void appendOrder(JPSQLModel m,StringBuffer jpql) {
        if(CollectionUtils.isEmpty(m.getOrders())) {
            return;
        }
        for (JPSQLModel.QueryOrder od : m.getOrders()) {
            jpql.append(" order by ").append(od.getName()).append(" ").append(od.getType() ? "asc" : "desc").append(", ");
        }
        jpql.deleteCharAt(jpql.length() - 1).deleteCharAt(jpql.length() - 1);
    }

    /**
     * 解析group语句
     * @param m JPSQLModel对象
     * @param jpql jpql语句buffer
     * @author zhangrb
     * @user zhangrb
     * @date 2019-08-26
     */
    private static void appendGroup(JPSQLModel m,StringBuffer jpql) {
        if(CollectionUtils.isEmpty(m.getGroups())) {
            return;
        }
        jpql.append(" group by ");
        for (String g : m.getGroups()) {
            jpql.append(g).append(", ");
        }
        jpql.deleteCharAt(jpql.length() - 1).deleteCharAt(jpql.length() - 1);
    }

    /**
     * 解析having语句
     * @param m JPSQLModel对象
     * @param jpql jpql语句buffer
     * @param paramList 参数列表对象
     * @author zhangrb
     * @user zhangrb
     * @date 2019-08-26
     */
    private static void appendHaving(JPSQLModel m,StringBuffer jpql, List<Object> paramList) {
        if(m.getHavingConditions().isEmpty()) {
            return;
        }
        jpql.append(" having ");
        boolean has = false;
        for (JPSQLModel.HavingCondition hc : m.getHavingConditions()) {
            jpql.append("(");
            for (JPSQLModel.HavingParam hp : hc.getHavingParams()) {
                has = true;
                if(hp.getFuncType() != null) {
                    jpql.append(hp.getFuncType().value()).append("(").append(hp.getName()).append(")");
                } else {
                    jpql.append(hp.getName());
                }
                if(Constants.GE_SF.equals(hp.getOp())) {
                    jpql.append(" >= ?").append(paramList.size() + 1);
                    paramList.add(hp.getValue());
                } else if(Constants.GT_SF.equals(hp.getOp())) {
                    jpql.append(" > ?").append(paramList.size() + 1);
                    paramList.add(hp.getValue());
                } else if(Constants.LT_SF.equals(hp.getOp())) {
                    jpql.append(" < ?").append(paramList.size() + 1);
                    paramList.add(hp.getValue());
                } else if(Constants.LE_SF.equals(hp.getOp())) {
                    jpql.append(" <= ?").append(paramList.size() + 1);
                    paramList.add(hp.getValue());
                } else if(Constants.NE_SF.equals(hp.getOp())) {
                    jpql.append(" != ?").append(paramList.size() + 1);
                    paramList.add(hp.getValue());
                } else if(Constants.BT_SF.equals(hp.getOp())) {
                    jpql.append(" between ?").append(paramList.size() + 1).append(" and ?").append(paramList.size() + 2);
                    // 如果是between and 语句，value值必须为list，且size==2
                    if(!(hp.getValue() instanceof List)) {
                        throw new JPQLModelException("between and 语句的value值必需是List，并且size==2");
                    }
                    List<Object> vl = (List<Object>)hp.getValue();
                    if(vl.size() < 2) {
                        throw new JPQLModelException("between and 语句的value值必需是List，并且size==2");
                    }
                    paramList.add(vl.get(0));
                    paramList.add(vl.get(1));
                } else{
                    jpql.append(" =?").append(paramList.size() + 1);
                    paramList.add(hp.getValue());
                }
                jpql.append(" and ");
            }
            if(hc.getHavingParams().isEmpty()) {
                jpql.deleteCharAt(jpql.length() - 1);
            } else {
                jpql.delete(jpql.length() - 4, jpql.length()) ;
                jpql.append(") or ");
            }
        }

        if(!has) {
            // 去having
            jpql.delete(jpql.length() - 7, jpql.length());
        } else {
            // 去 or
            jpql.delete(jpql.length() - 3, jpql.length());
        }
    }

    /**
     * 解析where语句
     * @param m JPSQLModel对象
     * @param jpql jpql语句buffer
     * @param paramList 参数列表对象
     * @author zhangrb
     * @user zhangrb
     * @date 2019-08-26
     */
    private static void appendWhere(JPSQLModel m,StringBuffer jpql, List<Object> paramList) {
        if(m.getQueryConditions().isEmpty()) {
            return;
        }
        boolean has = false;
        jpql.append(" where ");
        for (JPSQLModel.QueryCondition qc : m.getQueryConditions()) {
            // 多个JPSQLModel.QueryCondition 使用or拼接
            jpql.append("(");
            Set<String> keySet = qc.getQueryParams().keySet();
            for (String key : keySet) {
                has = true;
                if(key.endsWith(Constants.GE_SF)) {
                    jpql.append(key.substring(0, key.length() - Constants.GE_SF.length())).append(" >= ?").append(paramList.size() + 1).append(" and ");
                    paramList.add(qc.getQueryParams().get(key));
                } else if(key.endsWith(Constants.GT_SF)) {
                    jpql.append(key.substring(0, key.length() - Constants.GT_SF.length())).append(" > ?").append(paramList.size() + 1).append(" and ");
                    paramList.add(qc.getQueryParams().get(key));
                } else if(key.endsWith(Constants.LT_SF)) {
                    jpql.append(key.substring(0, key.length() - Constants.LT_SF.length())).append(" < ?").append(paramList.size() + 1).append(" and ");
                    paramList.add(qc.getQueryParams().get(key));
                } else if(key.endsWith(Constants.LE_SF)) {
                    jpql.append(key.substring(0, key.length() - Constants.LE_SF.length())).append(" <= ?").append(paramList.size() + 1).append(" and ");
                    paramList.add(qc.getQueryParams().get(key));
                } else if(key.endsWith(Constants.NE_SF)) {
                    jpql.append(key.substring(0, key.length() - Constants.NE_SF.length())).append(" != ?").append(paramList.size() + 1).append(" and ");
                    paramList.add(qc.getQueryParams().get(key));
                } else if(key.endsWith(Constants.CONS_SF)) {
                    jpql.append(key.substring(0, key.length() - Constants.CONS_SF.length())).append(" like ?").append(paramList.size() + 1).append(" and ");
                    paramList.add("%" + qc.getQueryParams().get(key) + "%");
                } else if(key.endsWith(Constants.BT_SF)) {
                    jpql.append(" (").append(key.substring(0, key.length() - Constants.BT_SF.length())).append(" between ?").append(paramList.size() + 1).
                            append(" and ?").append(paramList.size() + 2).append(" ) ").append(" and ");
                    // 此处value应该是一个list，并且size=2
                    if(!(qc.getQueryParams().get(key) instanceof List)) {
                        throw new JPQLModelException("between and 语句的参数值必须是一个list，并且size==2");
                    }
                    List<Object> vl = (List<Object>)qc.getQueryParams().get(key);
                    if(vl.size() < 2) {
                        throw new JPQLModelException("between and 语句的参数值必须是一个list，并且size==2");
                    }
                    paramList.add(vl.get(0));
                    paramList.add(vl.get(1));
                } else if(key.endsWith(Constants.EMPTY_SF)) {
                    jpql.append(key.substring(0, key.length() - Constants.EMPTY_SF.length())).append(" is null ").append(" and ");
                } else if(key.endsWith(Constants.NOTEMPTY_SF)) {
                    jpql.append(key.substring(0, key.length() - Constants.NOTEMPTY_SF.length())).append(" is not null ").append(" and ");
                } else if(key.endsWith(Constants.EW_SF)) {
                    jpql.append(key.substring(0, key.length() - Constants.EW_SF.length())).append(" like ?").append(paramList.size() + 1).append(" and ");
                    paramList.add("%" + qc.getQueryParams().get(key));
                } else if(key.endsWith(Constants.SW_SF)) {
                    jpql.append(key.substring(0, key.length() - Constants.SW_SF.length())).append(" like ?").append(paramList.size() + 1).append(" and ");
                    paramList.add(qc.getQueryParams().get(key) + "%");
                } else if(key.endsWith(Constants.NOTIN_SF)) {
                    jpql.append(key.substring(0, key.length() - Constants.NOTIN_SF.length())).append(" not in ?").append(paramList.size() + 1).append(" and ");
                    // 此处value应该是一个list
                    if(!(qc.getQueryParams().get(key) instanceof List)) {
                        throw new JPQLModelException("not in 语句的参数值必须是一个list");
                    }
                    List<Object> vl = (List<Object>)qc.getQueryParams().get(key);
                    paramList.add(vl);
                } else if(key.endsWith(Constants.IN_SF)) {
                    jpql.append(key.substring(0, key.length() - Constants.IN_SF.length())).append(" in ?").append(paramList.size() + 1).append(" and ");
                    // 此处value应该是一个list
                    if(!(qc.getQueryParams().get(key) instanceof List)) {
                        throw new JPQLModelException("in 语句的参数值必须是一个list");
                    }
                    List<Object> vl = (List<Object>)qc.getQueryParams().get(key);
                    paramList.add(vl);
                } else {
                    jpql.append(key).append(" = ?").append(paramList.size() + 1).append(" and ");
                    paramList.add(qc.getQueryParams().get(key));
                }
            }
            if(!keySet.isEmpty()) {
                // 去and
                jpql.delete(jpql.length() - 4, jpql.length());
                jpql.append(") or ");
            } else {
                // 去 (
                jpql.deleteCharAt(jpql.length() - 1);
            }
        }
        if(!has) {
            // 去where
            jpql.delete(jpql.length() - 6, jpql.length());
        } else {
            // 去 or
            jpql.delete(jpql.length() - 3, jpql.length());
        }
    }

    /**
     * 解析查询结果语句
     * @param m JPSQLModel对象
     * @param jpql jpql语句buffer
     * @author zhangrb
     * @user zhangrb
     * @date 2019-08-26
     */
    private static void appendResult(JPSQLModel m,StringBuffer jpql) {
        jpql.append("select ");
        if(CollectionUtils.isEmpty(m.getResult())) {
            throw new JPQLModelException("未指定查询结果字段！");
        }
        for (JPSQLModel.QueryProperty p : m.getResult()) {
            if(p.getFuncType() != null) {
                jpql.append(p.getFuncType().value()).append("(").append(p.getName()).append(") as ").append(StringUtils.isEmpty(p.getAlias()) ? p.getName() : p.getAlias());
            } else {
                jpql.append(p.getName()).append(" as ").append(StringUtils.isEmpty(p.getAlias()) ? p.getName() : p.getAlias());
            }
            jpql.append(", ");
        }
        if(!m.getResult().isEmpty()) {
            jpql.deleteCharAt(jpql.length() - 1).deleteCharAt(jpql.length() - 1);
        }
    }

    private static void appendForm(JPSQLModel m,StringBuffer jpql) {
        if(m.getRootEntity() == null) {
            log.error("没有from语句，解析JPSQLModel失败");
            throw new JPQLModelException("没有from语句，解析JPSQLModel失败");
        }
        jpql.append(" from ").append(m.getRootEntity().getEntityClass().getSimpleName()).append(" as ").append(StringUtils.isEmpty(m.getRootEntity().getAlias()) ?
                m.getRootEntity().getEntityClass().getSimpleName() : m.getRootEntity().getAlias());
        JPSQLModel.EntityJoin ej = m.getEntityJoin();

        while (ej != null) {
            jpql.append(" ").append(ej.getJoinType().value()).append(" ").append(ej.getEntity().getEntityClass().getSimpleName()).append(" as ").
                    append(StringUtils.isEmpty(ej.getEntity().getAlias()) ? ej.getEntity().getEntityClass().getSimpleName() : ej.getEntity().getAlias()).append(" on ");
            boolean has = false;
            for (JPSQLModel.JoinCondtion jc : ej.getJoinCondtions()) {
                jpql.append("(");
                Set<String> keySet = jc.getJoinParam().keySet();
                for(String key : keySet) {
                    has = true;
                    if(key.endsWith(Constants.GE_SF)) {
                        jpql.append(key.substring(0, key.length() - Constants.GE_SF.length())).append(" >= ");
                    } else if(key.endsWith(Constants.GT_SF)) {
                        jpql.append(key.substring(0, key.length() - Constants.GT_SF.length())).append(" > ");
                    } else if(key.endsWith(Constants.LT_SF)) {
                        jpql.append(key.substring(0, key.length() - Constants.LT_SF.length())).append(" < ");
                    } else if(key.endsWith(Constants.LE_SF)) {
                        jpql.append(key.substring(0, key.length() - Constants.LE_SF.length())).append(" <= ");
                    } else if(key.endsWith(Constants.NE_SF)) {
                        jpql.append(key.substring(0, key.length() - Constants.NE_SF.length())).append(" != ");
                    } else {
                        // 其他格式当做普通字段处理
                        jpql.append(key).append(" = ");
                    }
                    jpql.append(jc.getJoinParam().get(key)).append(" and ");
                }
                if(!keySet.isEmpty()) {
                    // 去 and
                    jpql.delete(jpql.length() - 4, jpql.length());
                    jpql.append(") or ");
                } else {
                    // 去(
                    jpql.deleteCharAt(jpql.length() - 1);
                }
            }
            if(has) {
                // 去 or
                jpql.delete(jpql.length() - 3, jpql.length());
            }
            ej = ej.getNextEntityJoin();
        }
    }

}
